const PENDING = "pending";
const FULLFILLED = "fulfilled";
const REJECTED = "rejected";

function runAsynctask(callback) {
  // 这里原本是需要使用MutationObserver的
  // 但是MutationObserver使用起来比较的复杂，所以这里就是用了这个queueMicrotask和setTimeout来模拟异步
  if (typeof queueMicrotask === "function") {
    queueMicrotask(callback); // 放在微任务队列中
  } else {
    setTimeout(callback, 0); // 放在宏任务队列中
  }
}

// function resolvePromise(p2, x, resolve, reject) {
//   // 判断x和p2是否指向同一个对象
//   if (x === p2) {
//     throw new TypeError("Chaining cycle detected for promise #<Promise>");
//   }
//   // 判断x是否是一个promise对象
//   if (x instanceof MyPromise) {
//     // 如果是就执行了then方法，将结果传给这个外面的MyPromise对象
//     x.then(
//       (res) => resolve(res),
//       (err) => reject(err)
//     );
//   } else {
//     resolve(x); // 调用resolve函数，将返回值传给下一个then
//   }
// }

// 符合Promise\A规范(考虑了各种边界情况)
function resolvePromise(p2, x, resolve, reject) {
  // 2.3.3.1 如果p2和x引用同一个对象,通过TypeError作为原因来拒绝pormise
  if (x === p2) {
    throw new TypeError("Chaining cycle detected for promise");
  }

  /**
   * 2.3.3.2 如果x是一个promise,采用他的状态
   *  2.3.3.3.1 如果x是pengding状态,promise必须保持等待状态,直到x被fulfilled或rejected
   *  2.3.3.3.2 如果x是fulfilled状态,用相同的原因解决promise
   *  2.3.3.3.3 如果x是rejected状态,用相同的原因拒绝promise
   * */
  if (x instanceof HMPromise) {
    x.then((y) => {
      resolvePromise(p2, y, resolve, reject);
    }, reject);
  }
  // 2.3.3 如果x是一个对象或者函数
  else if (x !== null && (typeof x === "object" || typeof x === "function")) {
    // 2.3.3.1 让then成为x.then
    try {
      var then = x.then;
    } catch (e) {
      // 2.3.3.2 如果检索属性x.then抛出了异常e，用e作为原因拒绝promise
      return reject(e);
    }

    /**
     * 2.3.3.3  如果then是一个函数，通过call调用他,并且将x作为他的this(参数1)
     * 调用then时传入2个回调函数:
     *    第一个参数叫做resolvePromise(对应到的参数2)
     *    第二个参数叫做rejectPromise(对应到参数3)
     * */

    if (typeof then === "function") {
      // 2.3.3.3.3 如果 resolvePromise 和 rejectPromise 均被调用，或者同一参数被调用了多次，只采用第一次调用,后续的调用会被忽略(观察called后续的赋值+判断)
      let called = false;
      try {
        then.call(
          x,
          // 2.3.3.3.1 如果 resolvePromise 以 成功原因 y 为参数被调用，继续执行 resolvePromise
          (y) => {
            if (called) return;
            called = true;
            resolvePromise(p2, y, resolve, reject);
          },
          // 2.3.3.3.2 如果 rejectPromise 以拒绝原因 r 为参数被调用，用 r 拒绝 promise
          (r) => {
            if (called) return;
            called = true;
            reject(r);
          }
        );
      } catch (e) {
        // 2.3.3.3.4 如果调用then抛出异常
        // 2.3.3.3.4.1 如果resolvePromise或rejectPromise已经被调用，忽略它
        if (called) return;
        called = true;

        // 2.3.3.3.4.2 否则以 e 作为拒绝原因 拒绝promise
        reject(e);
      }
    } else {
      // 2.3.3.4 如果then不是函数，用 x 作为原因 兑现promise
      resolve(x);
    }
  } else {
    // 2.3.4 如果then不是对象或函数，用 x 作为原因 兑现promise
    return resolve(x);
  }
}

class MyPromise {
  // 添加状态
  state = PENDING;
  // 添加原因，保存成功或者失败的原因
  reason = undefined;
  // 保存成功或者失败的回调函数，用于实现异步
  #handlers = [];
  // 构造函数
  constructor(func) {
    const resolve = (value) => {
      if (this.state === PENDING) {
        this.state = FULLFILLED;
        this.reason = value;
        // 执行成功的回调函数
        this.#handlers.forEach(({ onFulfilled }) => {
          onFulfilled(this.reason);
        });
      }
    };

    const reject = (reason) => {
      if (this.state === PENDING) {
        this.state = REJECTED;
        this.reason = reason;
        // 执行失败的回调函数
        this.#handlers.forEach(({ onRejected }) => {
          onRejected(this.reason);
        });
      }
    };

    try {
      func(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }
  // then方法
  then(onFulfilled, onRejected) {
    // 处理成功的回调函数，如果没有传入，就赋值一个恒等的函数
    onFulfilled =
      typeof onFulfilled === "function" ? onFulfilled : (value) => value;
    // 处理失败的回调函数，如果没有传入，就抛出错误
    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : (reason) => {
            throw reason;
          };

    const p2 = new MyPromise((resolve, reject) => {
      // 在这里面的函数方法是立刻同步执行的
      // 但是放在外面也可以，之所以放在这里是因为这里可以获取到resolve和reject函数
      // 根据状态执行对应的回调函数
      if (this.state === FULLFILLED) {
        // 在执行成功的回调之前，就直接将回调函数放入微任务队列中
        runAsynctask(() => {
          try {
            const x = onFulfilled(this.reason); // 保存成功的返回值
            /* // 判断x和p2是否指向同一个对象
                if (x === p2) {
                  throw new TypeError('Chaining cycle detected for promise #<Promise>')
                }
                // 判断x是否是一个promise对象
                if (x instanceof MyPromise) {
                  // 如果是就执行了then方法，将结果传给这个外面的MyPromise对象
                  x.then(res => resolve(res), err => reject(err));
                } else {
                  resolve(x); // 调用resolve函数，将返回值传给下一个then
                } */
            resolvePromise(p2, x, resolve, reject);
          } catch (err) {
            reject(err); // 调用reject函数，将错误传给下一个then
          }
        });
      } else if (this.state === REJECTED) {
        // 在执行失败的回调之前，就直接将回调函数放入微任务队列中
        runAsynctask(() => {
          try {
            const x = onRejected(this.reason); // 保存失败的原因

            resolvePromise(p2, x, resolve, reject);
          } catch (err) {
            reject(err); // 调用reject函数，将错误传给下一个then
          }
        });
      } else if (this.state === PENDING) {
        // 代码能走到这里说明这里这里就需要异步执行
        this.#handlers.push({
          // 将需要异步执行的函数变成对象放入数组中
          onFulfilled: () => {
            // 在这里如果是直接使用了runAsynctask(() => { onFulfilled(this.reason); })会直接执行而不是异步在调用的时候执行
            // 在这里是直接使用一个箭头函数，而不是一个runAsynctask(() => { onFulfilled(this.reason); })
            // 因为如果直接使用这个函数的话，会导致函数走到这里的时候直接执行而不是异步执行
            runAsynctask(() => {
              try {
                const x = onFulfilled(this.reason);
                resolvePromise(p2, x, resolve, reject);
              } catch (err) {
                reject(err);
              }
            });
          },
          onRejected: () => {
            runAsynctask(() => {
              try {
                const x = onRejected(this.reason);
                resolvePromise(p2, x, resolve, reject);
              } catch (err) {
                reject(err);
              }
            });
          },
        });
      }
    });

    return p2;
  }
  // catch方法
  catch(onRejected) {
    return this.then(null, onRejected);
  }
  // finally方法
  finally(onFinally) {
    return this.then(onFinally, onFinally);
  }
  // resolve方法
  static resolve(value) {
    if (value instanceof MyPromise) {
      return value;
    }

    return new MyPromise((resolve) => {
      resolve(value);
    });
  }
  // reject方法
  static reject(reason) {
    return new MyPromise((undefined, reject) => {
      reject(reason);
    });
  }
  // race方法
  static race(promises) {
    return new MyPromise((resolve, reject) => {
      // 判断promises是否是一个数组
      if (!Array.isArray(promises)) {
        return reject(new TypeError("Argument is not iterable"));
      }

      promises.forEach((promise) => {
        // 这里调用resolve方法，是为了让每一个promise方法都可以使用.then方法
        MyPromise.resolve(promise).then(
          (res) => {
            resolve(res); // 敲定返回的MyPromise对象的状态
          },
          (err) => {
            reject(err); // 敲定返回的MyPromise对象的状态
          }
        );
      });
    });
  }
  // all方法
  static all(promises) {
    return new MyPromise((resolve, reject) => {
      // 判断promises是否是一个数组
      if (!Array.isArray(promises)) {
        return reject(new TypeError("Argument is not iterable"));
      }
      // 空数组直接兑现
      promises.length === 0 && resolve(promises);

      let arr = [];
      let count = 0;

      promises.forEach((promise, index) => {
        MyPromise.resolve(promise).then(
          (res) => {
            arr[index] = res;
            count++;
            count === promises.length && resolve(arr);
          },
          (err) => {
            reject(err);
          }
        );
      });
    });
  }
  // allSettled方法
  static allSettled(promises) {
    return new MyPromise((resolve, reject) => {
      // 判断promises是否是一个数组
      if (!Array.isArray(promises)) {
        return reject(new TypeError("Argument is not iterable"));
      }

      // 空数组直接兑现
      promises.length === 0 && resolve(promises);

      let arr = [];
      let count = 0;
      promises.forEach((promise, index) => {
        MyPromise.resolve(promise).then(
          (res) => {
            arr[index] = { status: FULLFILLED, value: res };
            count++;
            count === promises.length && resolve(arr);
          },
          (err) => {
            arr[index] = { status: REJECTED, reason: err };
            count++;
            count === promises.length && resolve(arr);
          }
        );
      });
    });
  }
  // any方法
  static any(promises) {
    return new MyPromise((resolve, reject) => {
      // 判断promises是否是一个数组
      if (!Array.isArray(promises)) {
        return reject(new TypeError("Argument is not iterable"));
      }

      // 空数组直接兑现
      promises.length === 0 &&
        reject(new AggregateError(promises, "All promises were rejected"));

      let arr = [];
      let count = 0;
      promises.forEach((promise, index) => {
        MyPromise.resolve(promise).then(
          (res) => {
            // 只要有一个成功了就返回第一个成功的结果
            resolve(res);
          },
          (err) => {
            arr[index] = err;
            count++;
            console.log(arr);
            count === promises.length &&
              reject(new AggregateError(arr, "All promises were rejected"));
          }
        );
      });
    });
  }
}
// 将我们自己手写的Promise拷贝到一个单独的文件,并在底部加上
module.exports = {
  deferred() {
    const res = {};
    // 自己手写的Promise
    res.promise = new MyPromise((resolve, reject) => {
      // 内部将resolve和reject赋值上去
      res.resolve = resolve;
      res.reject = reject;
    });
    return res;
  },
};
